/**
 *******************************************************************************************************************************************
 * @file        dev_uart.c
 * @brief       调试日志串口打印
 * @details     /
 * @since       Change Logs:
 * Date         Author       Notes
 * 2022-12-08   lzh          the first version
 * 2023-02-23   lzh          优化 SWM34SCE 端口初始化配置流程
 * @remark      /
 *******************************************************************************************************************************************
 * @attention
 *
 * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS WITH CODING INFORMATION
 * REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. AS A RESULT, SYNWIT SHALL NOT BE HELD LIABLE
 * FOR ANY DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT
 * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION CONTAINED HEREIN IN CONN-
 * -ECTION WITH THEIR PRODUCTS.
 *
 * @copyright   2012 Synwit Technology
 *******************************************************************************************************************************************
 */
#include "dev_uart.h"
#include "version.h"

/** 用作调试日志打印的串口号 */
#define DEBUG_UART_X              UART0

/** 用作调试日志打印的串口波特率（data-8, stop-1, no parity） */
#define DEBUG_UART_BAUD           9600

#if (SWM34S_LCM_PCBV == SWM34SCE_PIN48_A2)
/* 接按键输入检测, 用作 SWD 端口切换 UART 与 SWD 功能 */
#define KEY_INPUT_GPIO        GPIOD
#define KEY_INPUT_PIN         PIN2

/**
 * @brief   串口（SWD）端口功能切换
 * @note    SWM34SCE_A2 SWD 与串口打印共用同一组端口, 选取 D2 在上电之初用作 串口与SWD功能切换 的输入检测口,
 * 设定一个窗口时间检测 D2 输入是否有效(高电平有效)
 * 若在窗口时限内, D2 输入高电平有效则将 SWD 端口配置为串口日志打印, 否则默认配置为 SWD 功能.
 */
static void uart_port_switch(void)
{
    static uint8_t init_flag = 0;
    if (init_flag == 0) /* 仅初次上电检测一次 */
    {
        /* SWM341 系列 GPIO A15/B2/B3/B4/B5/C0/C1/C2 提供了 IO 滤波功能, 且可配置滤波 IO、滤波时钟、滤波窗口时间等 */
        GPIO_Init(KEY_INPUT_GPIO, KEY_INPUT_PIN, 0, 0, 1, 0); /* 输入 & 内部下拉 */
        uint32_t valid = 0;
        uint32_t window_time = 666; /* 对上电启动影响较小 */
        for (uint32_t time = 0; time < window_time; ++time)
        {
            valid += GPIO_GetBit(KEY_INPUT_GPIO, KEY_INPUT_PIN); /* 高电平有效 */
            for (uint32_t i = 0; i < 88; ++i) __NOP();  /* 简单去抖 */
        }
        if (valid >= (window_time >> 1))   /* 大于一半的窗口时间视为有效 */
        {
            init_flag = 1;
        }
        else
        {
            init_flag = 2;
        }
    }
    if (1 == init_flag)
    {
        /* SWD 端口切换为 GPIO 或 其它功能时必须加上这句, 未在固件库中开放给用户, 避免用户配置端口错误而导致无法下载 */
        *((volatile uint32_t *)0x40000190) = 0;
        PORT_Init(PORTB, PIN12, PORTB_PIN12_UART0_RX, 1); /* GPIOB.12 配置为 UART0 输入引脚 */
        PORT_Init(PORTB, PIN14, PORTB_PIN14_UART0_TX, 0); /* GPIOB.14 配置为 UART0 输出引脚 */
    }
    else if (2 == init_flag)
    {
        /* 如要恢复 SWD 端口功能必须加上这句, 未在固件库中开放给用户(重新上电后, SWD 端口总会自动恢复为 SWD 功能) */
        *((volatile uint32_t *)0x40000190) = 1;
        PORT_Init(PORTB, PIN14, PORTB_PIN14_SWDIO, 1);
        PORT_Init(PORTB, PIN12, PORTB_PIN12_SWDCK, 1);
    }
}
#endif

/**
 * @brief   配置串口打印调试日志的端口
 */
void uart_debug_port_init(void)
{
    #if (SWM34S_LCM_PCBV == SWM34SCE_PIN48_A2) /* D2 接高复位上电时, 将 SWD/SWC 端口复用为 调试串口打印 <端口十分紧张> */
    uart_port_switch();
    #else
    GPIO_Init(GPIOM, PIN0, 0, 1, 0, 0);
    PORT_Init(PORTM, PIN0, PORTM_PIN0_UART0_RX, 1); /* GPIOM.0 配置为 UART0 输入引脚 */
    PORT_Init(PORTM, PIN1, PORTM_PIN1_UART0_TX, 0); /* GPIOM.1 配置为 UART0 输出引脚 */
    PORT_Init(PORTM, PIN3, PORTM_PIN3_GPIO, 0);
    GPIO_Init(GPIOM, PIN3, 1, 1, 0, 0);
    GPIOM->DATAPIN3 =  0 ;//默认为接收状态
    #endif
}

/**
 * @brief   初始化用作打印调试日志的串口
 */
void uart_debug_init(void)
{
    uart_debug_port_init();
    UART_InitStructure UART_initStruct;
    UART_initStruct.Baudrate = DEBUG_UART_BAUD;
    UART_initStruct.DataBits = UART_DATA_8BIT;
    UART_initStruct.Parity = UART_PARITY_NONE;
    UART_initStruct.StopBits = UART_STOP_1BIT;
    UART_initStruct.RXThreshold = 0;
    UART_initStruct.RXThresholdIEn = 1;//接收中断
    UART_initStruct.TXThreshold = 3;
    UART_initStruct.TXThresholdIEn = 0;
    UART_initStruct.TimeoutTime = 12;
    UART_initStruct.TimeoutIEn = 1; //10个字符时间内未接收到新的数据则触发超时中断
    UART_Init(DEBUG_UART_X, &UART_initStruct);
    UART_Open(DEBUG_UART_X);
}
/**
 * @brief   配置串口打印调试日志的端口
 */
void uart_blue_port_init(void)
{
    GPIO_Init(GPIOB, PIN13, 0, 1, 0, 0);
    GPIO_Init(GPIOB, PIN11, 1, 1, 0, 0);
    PORT_Init(PORTB, PIN13, PORTB_PIN13_UART2_RX, 1); /* GPIOM.0 配置为 UART0 输入引脚 */
    PORT_Init(PORTB, PIN11, PORTB_PIN11_UART2_TX, 0); /* GPIOM.1 配置为 UART0 输出引脚 */
}

/**
 * @brief   初始化用作打印调试日志的串口
 */
void uart_blue_init(void)
{
    uart_blue_port_init();
    UART_InitStructure UART_initStruct;
    UART_initStruct.Baudrate = 57600;
    UART_initStruct.DataBits = UART_DATA_8BIT;
    UART_initStruct.Parity = UART_PARITY_NONE;
    UART_initStruct.StopBits = UART_STOP_1BIT;
    UART_initStruct.RXThreshold = 0;
    UART_initStruct.RXThresholdIEn = 1;//接收中断
    UART_initStruct.TXThreshold = 3;
    UART_initStruct.TXThresholdIEn = 0;
    UART_initStruct.TimeoutTime = 10;
    UART_initStruct.TimeoutIEn = 1; //10个字符时间内未接收到新的数据则触发超时中断
    UART_Init(UART2, &UART_initStruct);
    UART_Open(UART2);
}
/**
 * @brief   串口发送数据
 * @param   uart_x    : 串口号
 * @param   src_buf   : 数据源
 * @param   num_bytes : 数据大小 / Bytes
 * @retval  /
 * @note    可自定义拓展为 类printf()方法
 */
void uart_send_msg(UART_TypeDef *uart_x, const uint8_t *src_buf, const uint32_t num_bytes)
{
    const uint8_t *ptr = src_buf;
    for (uint32_t i = 0; i < num_bytes; i++)
    {
        if (UART_IsTXFIFOFull(uart_x) == 0)
        {
            UART_WriteByte(uart_x, *ptr++);
        }
        while (UART_IsTXBusy(uart_x))
            ; /* 忙查询 */
    }
}

#if !defined(__MICROLIB) /* printf(...) 的两种重定向实现: 标准C库      MicroLIB C库 */
/**
 * @brief   不使用 MicroLIB 时避免使用半主机模式
 * @note    #pragma import(__use_no_semihosting)
 */
__asm(".global __use_no_semihosting\n\t");
void _sys_exit(int x)
{
    x = x;
}

/* __use_no_semihosting was requested, but _ttywrch was */
void _ttywrch(int ch)
{
    ch = ch;
}

/*
struct __FILE
{
  int handle;
};*/

FILE __stdout;

#endif /* !defined(__MICROLIB) */

#if defined(__GNUC__) && !defined(__clang__)
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* defined ( __GNUC__ ) && !defined (__clang__)  */

PUTCHAR_PROTOTYPE /* 实现串口发送一个字节数据的函数 */
{
    UART_WriteByte(DEBUG_UART_X, ch);

    while (UART_IsTXBusy(DEBUG_UART_X))
        ;

    return ch;
}


